/** @file    SecurityToken.cpp
 *  @author  Alessandro Polo
 *  @version $Id: SecurityToken.cpp 2826 2010-08-02 10:13:45Z alex $
 *  @brief
 * File containing methods for the wosh::SecurityToken class.
 * The header for this class can be found in SecurityToken.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <core/SecurityToken.h>

 #include <core/ObjectAllocator.h>
 #include <core/Utilities.h>
 #include <core/WoshKernel.h>
 #include <core/Message.h>


using namespace std;
namespace wosh {

 WOSH_REGISTER_INTERFACE(wosh::SecurityToken, "wosh::ISerializable", 1.01, _static_SecurityToken)

 WOSH_REGISTER_INTERFACE(wosh::SecurityTokenProvider, "", 1.01, _static_SecurityTokenProvider)
 WOSH_REGISTER_INTERFACE(wosh::SecurityTokenAuthenticator, "", 1.01, _static_SecurityTokenAuthenticator)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

SecurityToken::SecurityToken() {
	this->init_signed = false;
	this->token_id = Utilities::randomLong();
	this->token_ts = Utilities::std_time();
	this->ttl = 0;
	
	this->host_name = WoshKernel::getKernelName();
	this->authenticator_ver = 0.0;
	this->authentication_mode = SecurityToken::AUTH_UNKNOWN;
 }

SecurityToken& SecurityToken::operator=(const SecurityToken& m) {
	if (this == &m) return *this; // Same object?
	this->init_signed = m.init_signed;
	this->token_id = m.token_id;
	this->token_ts = m.token_ts;
	this->ttl = m.ttl;
	this->user_name = m.user_name;
	this->creator_uuid = m.creator_uuid;
	this->target_uuid = m.target_uuid;
	this->target_hash = m.target_hash;
	this->provider_uuid = m.provider_uuid;
	this->host_name = m.host_name;
	this->authenticator_classname = m.authenticator_classname;
	this->authenticator_ver = m.authenticator_ver;
	this->authentication_mode = m.authentication_mode;
	return *this;
 }

bool SecurityToken::operator==(const SecurityToken &other) const {
	if ( this->token_id != other.token_id )	return false;
	if ( this->token_ts != other.token_ts )	return false;
	if ( this->ttl != other.ttl ) return false;
	if ( this->user_name != other.user_name ) return false;
	if ( this->creator_uuid != other.creator_uuid ) return false;
	if ( this->target_uuid != other.target_uuid ) return false;
	if ( this->target_hash != other.target_hash ) return false;
	if ( this->provider_uuid != other.provider_uuid ) return false;
	if ( this->host_name != other.host_name ) return false;
	if ( this->authenticator_classname != other.authenticator_classname ) return false;
	if ( this->authentication_mode != other.authentication_mode ) return false;
	return true;
 }

bool SecurityToken::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = true;
	ret = hash_context->update( this->token_id ) & ret;
	ret = hash_context->update( this->token_ts ) & ret;
	ret = hash_context->update( this->ttl ) & ret;
	ret = hash_context->update( this->user_name ) & ret;
	ret = hash_context->update( this->creator_uuid ) & ret;
	ret = hash_context->update( this->target_uuid ) & ret;
	ret = hash_context->update( this->target_hash ) & ret;
	ret = hash_context->update( this->provider_uuid ) & ret;
	ret = hash_context->update( this->host_name ) & ret;
	ret = hash_context->update( this->authenticator_classname ) & ret;
	ret = hash_context->update( this->authenticator_ver ) & ret;
	ret = hash_context->update( this->authentication_mode ) & ret;
	return ret;
 }

std::string SecurityToken::toString() const {
	return Utilities::format("%s#%"PRId64"[%s]", getClassNameOnlyOf(getClassName()), this->token_id, this->user_name.c_str() );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

SecurityTokenProvider::SecurityTokenProvider() {
	Utilities::UUID( this->uuid );
 }

WRESULT SecurityTokenProvider::setAuthenticationMode( SecurityToken::AUTHENTICATION_MODE mode, const std::string& mode_options ) {
	this->auth_mode = mode;
	this->auth_mode_options = mode_options;
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

SecurityToken* SecurityTokenProvider::createToken( const Object* creator, const std::string& username ) const {
	SecurityToken* st = createToken();
	initToken(st);
	updateToken(st, creator);
	if ( !username.empty() )
		updateToken(st, username);
	return st;
 }

void SecurityTokenProvider::initToken( SecurityToken* st ) const {
	if ( st == NULL ) return;
	st->token_id = Utilities::randomLong();
	st->token_ts = Utilities::std_time();
	st->host_name = WoshKernel::getKernelName();
	st->provider_uuid = this->uuid;
	st->authenticator_classname = getClassName();
	st->authenticator_ver = getVersion();
 }

void SecurityTokenProvider::updateToken( SecurityToken* st, const std::string& username ) const {
	if ( st == NULL ) return;
	st->user_name = username;
	st->creator_uuid = "";
	st->target_uuid = "";
 }

void SecurityTokenProvider::updateToken( SecurityToken* st, const Object* creator ) const {
	if ( st == NULL || creator == NULL ) return;
	st->creator_uuid = creator->getEntityID();
	st->user_name = creator->getPermission().getOwner();
 }

void SecurityTokenProvider::setupTokenMessage( SecurityToken* st, Message* message, const Object* creator ) const {
	if ( st == NULL || message == NULL || creator == NULL ) return;
	updateToken(st, creator);
	st->target_uuid = Utilities::format("MESSAGE_%s#%"PRId64"", message->getClassName(), message->getID() );

	if ( message->getSourceUUID().empty() )
		message->setSourceUUID(creator->getEntityID());
 }

void SecurityTokenProvider::finalizeSigning( SecurityToken* st, Message* message ) const {
	if ( st == NULL || message == NULL ) return;
	message->setSecurityToken(st);
	DataHashContext_SHA512().calculateHash(message, st->target_hash);
	///@bug BUG TODO  st->sign();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

SecurityToken* SecurityTokenProvider::signMessage( Message* message, const Object* creator ) const {
	if ( message == NULL || creator == NULL ) return NULL;
	SecurityToken* st = createToken();
	initToken(st);
	setupTokenMessage(st, message, creator);
	finalizeSigning(st, message);
	return st;
 }

SecurityToken* SecurityTokenProvider::signMessage( Message* message, const std::string& username ) const {
	if ( message == NULL ) return NULL;
	SecurityToken* st = createToken();
	initToken(st);
	updateToken(st, username);
	finalizeSigning(st, message);
	return st;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT SecurityTokenAuthenticator::validate( const Object* target, const SecurityToken* token ) const {
	if ( target == NULL || token == NULL ) return WRET_ERR_PARAM;
	if ( token->target_uuid != target->getEntityID() )
		return WRET_ERR_INTERNAL;
	std::string temp_hash;
	DataHashContext_SHA512().calculateHash(target, temp_hash);
	if ( temp_hash == token->target_hash )
		return WRET_OK;
	return WRET_ERR_INTERNAL;
 }

WRESULT SecurityTokenAuthenticator::validateMessage( const Message* message ) const {
	if ( message == NULL ) return WRET_ERR_PARAM;
	if ( !message->hasSecurityToken() ) return WRET_ERR_PARAM;
	std::string temp_hash;
	DataHashContext_SHA512().calculateHash(message, temp_hash);
	if ( temp_hash == message->getSecurityToken()->target_hash )
		return WRET_OK;
	return WRET_ERR_INTERNAL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// SecurityTokenProviderImplicit

 WOSH_REGISTER(wosh::SecurityTokenImplicit, "wosh::SecurityToken", 1.01, _static_SecurityTokenImplicit)
 WOSH_REGISTER(wosh::SecurityTokenProviderImplicit, "wosh::SecurityTokenProvider", 1.01, _static_SecurityTokenProviderImplicit)

SecurityToken* SecurityTokenProviderImplicit::createToken() const {
	SecurityTokenImplicit* st = new SecurityTokenImplicit();
	return st;
 }

/////////////////////////////////////////////////////////////////////////////// SecurityTokenProviderImplicit
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////// SecurityTokenProviderProxy

 WOSH_REGISTER(wosh::SecurityTokenProxy, "wosh::SecurityToken", 1.01, _static_SecurityTokenProxy)
 WOSH_REGISTER(wosh::SecurityTokenProviderProxy, "wosh::SecurityTokenProvider", 1.01, _static_SecurityTokenProviderProxy)

void SecurityTokenProxy::setSource( SecurityToken* src_token ) {
	if ( this->source_token != NULL )
		delete this->source_token;
	this->source_token = src_token;
 }

SecurityToken* SecurityTokenProviderProxy::createToken() const {
	SecurityTokenProxy* st = new SecurityTokenProxy();
	return st;
 }

SecurityToken* SecurityTokenProviderProxy::signMessage( Message* message, const SecurityToken* source_token, const Object* creator ) const {
	if ( message == NULL || creator == NULL ) return NULL;
	if ( source_token == NULL ) return SecurityTokenProvider::signMessage(message, creator);
	SecurityTokenProxy* st = new SecurityTokenProxy();
	initToken(st);
	st->setSource( source_token->clone() );
	setupTokenMessage(st, message, creator);
	finalizeSigning(st, message);
	return st;
 }

SecurityToken* SecurityTokenProviderProxy::signMessage( Message* message, const SecurityToken* source_token, const std::string& username ) const {
	if ( message == NULL ) return NULL;
	if ( source_token == NULL ) return SecurityTokenProvider::signMessage(message, username);
	SecurityTokenProxy* st = new SecurityTokenProxy();
	initToken(st);
	st->setSource( source_token->clone() );
	updateToken(st, username);
	finalizeSigning(st, message);
	return st;
 }

////////////////////////////////////////////////////////////////////////////////// SecurityTokenProviderProxy
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
